package com.tpvlog.dfs.client;

import com.tpvlog.dfs.client.req.FileInfo;
import com.tpvlog.dfs.client.req.Host;
import com.tpvlog.dfs.client.req.NetworkRequest;
import com.tpvlog.dfs.client.resp.NetworkResponse;
import com.tpvlog.dfs.client.resp.ResponseCallback;

import java.nio.ByteBuffer;
import java.util.UUID;

/**
 * NIO Client，负责跟DataNode进行网络通信
 *
 * @author Ressmix
 */
public class NIOClient {

    private NetworkManager networkManager;

    public NIOClient() {
        this.networkManager = new NetworkManager();
    }

    /**
     * 上传文件
     */
    public void sendFile(Host host, FileInfo fileInfo, ResponseCallback callback) {
        // 1.尝试与HOST建立连接，阻塞等待直到建立连接或失败
        if (!networkManager.tryConnect(host)) {
            throw new RuntimeException("建立连接失败");
        }
        // 2.异步发送请求
        NetworkRequest request = createUploadFileRequest(host, fileInfo, callback);
        networkManager.sendRequest(request);
    }

    /**
     * 下载文件
     */
    public byte[] readFile(Host host, FileInfo fileInfo) throws Exception {
        // 1.尝试与HOST建立连接，阻塞等待直到建立连接或失败
        if (!networkManager.tryConnect(host)) {
            throw new RuntimeException("建立连接失败");
        }

        // 2.异步发送请求
        NetworkRequest request = createDownloadFileRequest(host, fileInfo, null);
        networkManager.sendRequest(request);

        // 3.阻塞等待响应
        NetworkResponse response = networkManager.waitResponse(request.getId());
        if (response.getError()) {
            throw new RuntimeException("响应异常");
        }
        return response.getBuffer().array();
    }

    /*----------------------------------------PRIVATE METHOD---------------------------------------*/

    /**
     * 创建一个上传文件的请求
     */
    private NetworkRequest createUploadFileRequest(Host host, FileInfo fileInfo, ResponseCallback callback) {
        ByteBuffer buffer = ByteBuffer.allocate(
                NetworkRequest.REQUEST_TYPE +
                        NetworkRequest.FILENAME_LENGTH +
                        fileInfo.getFilename().getBytes().length +
                        NetworkRequest.FILE_LENGTH +
                        fileInfo.getFileLength());
        buffer.putInt(NetworkRequest.REQUEST_SEND_FILE);
        buffer.putInt(fileInfo.getFilename().getBytes().length);
        buffer.put(fileInfo.getFilename().getBytes());
        buffer.putLong(fileInfo.getFileLength());
        buffer.put(fileInfo.getFile());
        buffer.rewind();

        NetworkRequest request = new NetworkRequest();
        // 请求ID，唯一标识
        request.setId(UUID.randomUUID().toString());
        request.setHostname(host.getHostname());
        request.setIp(host.getIp());
        request.setNioPort(host.getNioPort());
        request.setRequestType(NetworkRequest.REQUEST_SEND_FILE);
        request.setBuffer(buffer);
        request.setNeedResponse(false);
        request.setCallback(callback);

        return request;
    }

    /**
     * 创建一个下载文件的请求
     */
    private NetworkRequest createDownloadFileRequest(Host host, FileInfo fileInfo, ResponseCallback callback) {
        NetworkRequest request = new NetworkRequest();

        byte[] filenameBytes = fileInfo.getFilename().getBytes();

        ByteBuffer buffer = ByteBuffer.allocate(
                NetworkRequest.REQUEST_TYPE +
                        NetworkRequest.FILENAME_LENGTH +
                        filenameBytes.length);
        buffer.putInt(NetworkRequest.REQUEST_READ_FILE);
        buffer.putInt(filenameBytes.length);
        buffer.put(filenameBytes);
        buffer.rewind();

        request.setId(UUID.randomUUID().toString());
        request.setHostname(host.getHostname());
        request.setIp(host.getIp());
        request.setNioPort(host.getNioPort());
        request.setRequestType(NetworkRequest.REQUEST_READ_FILE);
        request.setBuffer(buffer);
        request.setNeedResponse(true);
        request.setCallback(callback);

        return request;
    }
}
